1+ import { unlinkSync , writeFileSync } from "node:fs" ;
2+ import { tmpdir } from "node:os" ;
3+ import { join } from "node:path" ;
14import { errors } from "../utils/errors" ;
2- import { exec , execWithStdin } from "../utils/shell" ;
5+ import { exec } from "../utils/shell" ;
36import type { CreateItemOptions , CreateItemResult , EditItemOptions } from "./types" ;
47
58interface VerboseOption {
@@ -82,10 +85,23 @@ interface OpItemResult {
8285 fields ?: Array < { label : string ; id : string ; type ?: string } > ;
8386}
8487
85- interface OpItemTemplate {
86- title : string ;
87- vault : { name : string } ;
88- category : string ;
88+ let tempCounter = 0 ;
89+
90+ function writeTempTemplate ( template : OpFieldsTemplate ) : string {
91+ const filePath = join ( tmpdir ( ) , `env2op-template-${ process . pid } -${ ++ tempCounter } .json` ) ;
92+ writeFileSync ( filePath , JSON . stringify ( template ) , "utf-8" ) ;
93+ return filePath ;
94+ }
95+
96+ function cleanupTempFile ( filePath : string ) : void {
97+ try {
98+ unlinkSync ( filePath ) ;
99+ } catch {
100+ // ignore cleanup errors
101+ }
102+ }
103+
104+ interface OpFieldsTemplate {
89105 fields : Array < {
90106 type : "STRING" | "CONCEALED" ;
91107 label : string ;
@@ -94,19 +110,12 @@ interface OpItemTemplate {
94110}
95111
96112/**
97- * Build JSON template for 1Password item (works for both create and edit)
113+ * Build JSON template containing only fields for 1Password item.
114+ * Metadata (title, vault, category) is passed via CLI flags.
98115 */
99- function buildItemTemplate (
100- title : string ,
101- vault : string ,
102- fields : Array < { key : string ; value : string } > ,
103- secret : boolean ,
104- ) : OpItemTemplate {
116+ function buildFieldsTemplate ( fields : Array < { key : string ; value : string } > , secret : boolean ) : OpFieldsTemplate {
105117 const fieldType = secret ? "CONCEALED" : "STRING" ;
106118 return {
107- title,
108- vault : { name : vault } ,
109- category : "SECURE_NOTE" ,
110119 fields : fields . map ( ( { key, value } ) => ( {
111120 type : fieldType ,
112121 label : key ,
@@ -121,11 +130,28 @@ function buildItemTemplate(
121130export async function createSecureNote ( options : CreateItemOptions & VerboseOption ) : Promise < CreateItemResult > {
122131 const { vault, title, fields, secret, verbose } = options ;
123132
124- const template = buildItemTemplate ( title , vault , fields , secret ) ;
125- const json = JSON . stringify ( template ) ;
133+ const template = buildFieldsTemplate ( fields , secret ) ;
134+ const templatePath = writeTempTemplate ( template ) ;
126135
127136 try {
128- const result = await execWithStdin ( "op" , [ "item" , "create" , "--format" , "json" ] , { stdin : json , verbose } ) ;
137+ const result = await exec (
138+ "op" ,
139+ [
140+ "item" ,
141+ "create" ,
142+ "--category" ,
143+ "Secure Note" ,
144+ "--title" ,
145+ title ,
146+ "--vault" ,
147+ vault ,
148+ "--template" ,
149+ templatePath ,
150+ "--format" ,
151+ "json" ,
152+ ] ,
153+ { verbose } ,
154+ ) ;
129155
130156 if ( result . exitCode !== 0 ) {
131157 throw new Error ( result . stderr || "Failed to create item" ) ;
@@ -151,6 +177,8 @@ export async function createSecureNote(options: CreateItemOptions & VerboseOptio
151177 } catch ( error ) {
152178 const message = error instanceof Error ? error . message : String ( error ) ;
153179 throw errors . itemCreateFailed ( message ) ;
180+ } finally {
181+ cleanupTempFile ( templatePath ) ;
154182 }
155183}
156184
@@ -162,14 +190,27 @@ export async function createSecureNote(options: CreateItemOptions & VerboseOptio
162190export async function editSecureNote ( options : EditItemOptions & VerboseOption ) : Promise < CreateItemResult > {
163191 const { vault, title, fields, secret, verbose, itemId } = options ;
164192
165- const template = buildItemTemplate ( title , vault , fields , secret ) ;
166- const json = JSON . stringify ( template ) ;
193+ const template = buildFieldsTemplate ( fields , secret ) ;
194+ const templatePath = writeTempTemplate ( template ) ;
167195
168196 try {
169- const result = await execWithStdin ( "op" , [ "item" , "edit" , itemId , "--format" , "json" ] , {
170- stdin : json ,
171- verbose,
172- } ) ;
197+ const result = await exec (
198+ "op" ,
199+ [
200+ "item" ,
201+ "edit" ,
202+ itemId ,
203+ "--title" ,
204+ title ,
205+ "--vault" ,
206+ vault ,
207+ "--template" ,
208+ templatePath ,
209+ "--format" ,
210+ "json" ,
211+ ] ,
212+ { verbose } ,
213+ ) ;
173214
174215 if ( result . exitCode !== 0 ) {
175216 throw new Error ( result . stderr || "Failed to edit item" ) ;
@@ -195,5 +236,7 @@ export async function editSecureNote(options: EditItemOptions & VerboseOption):
195236 } catch ( error ) {
196237 const message = error instanceof Error ? error . message : String ( error ) ;
197238 throw errors . itemEditFailed ( message ) ;
239+ } finally {
240+ cleanupTempFile ( templatePath ) ;
198241 }
199242}
0 commit comments