arcane-os 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
- package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
- package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
- package/browser-runtime/ai/browser-speech.mjs +2 -0
- package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
- package/browser-runtime/ai/model-controller.mjs +285 -95
- package/browser-runtime/ai/speech-worker-client.mjs +247 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
- package/browser-runtime/event-manager.mjs +1097 -1
- package/docs/architecture.md +2 -2
- package/docs/event-manager.md +155 -27
- package/docs/reference/README.md +27 -27
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1162 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +29 -6
- package/docs/reference/cli.md +117 -9
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/event-manager.md +577 -32
- package/docs/reference/inventory/package-api.json +478 -2
- package/docs/reference/inventory/runtime-components.json +108 -44
- package/docs/reference/inventory/runtime-modules.json +131 -53
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +157 -43
- package/docs/reference/runtime-components.md +258 -83
- package/docs/reference/runtime-modules.md +613 -77
- package/docs/reference/sdk-api.md +1014 -25
- package/package.json +5 -4
- package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
- package/runtime/arcane/components/app-bar.html +34 -13
- package/runtime/arcane/components/assistant-panel.html +110 -57
- package/runtime/arcane/components/calculator.html +7 -4
- package/runtime/arcane/components/chart.html +58 -17
- package/runtime/arcane/components/chat.html +606 -136
- package/runtime/arcane/components/conversation-view.html +13 -6
- package/runtime/arcane/components/dashboard-config.html +96 -59
- package/runtime/arcane/components/data-maintenance.html +69 -14
- package/runtime/arcane/components/data-view.html +53 -7
- package/runtime/arcane/components/directory-picker.html +118 -32
- package/runtime/arcane/components/document-inspector.html +47 -10
- package/runtime/arcane/components/file-drop.html +81 -35
- package/runtime/arcane/components/file-inspector.html +72 -22
- package/runtime/arcane/components/file-manager.html +374 -79
- package/runtime/arcane/components/integration-settings.html +12 -5
- package/runtime/arcane/components/local-ai-status.html +48 -19
- package/runtime/arcane/components/markdown-document.html +161 -68
- package/runtime/arcane/components/markdown-editor.html +110 -33
- package/runtime/arcane/components/media-embed.html +8 -5
- package/runtime/arcane/components/modal.html +15 -5
- package/runtime/arcane/components/output-panel.html +28 -23
- package/runtime/arcane/components/preferences-form.html +22 -4
- package/runtime/arcane/components/record-timeline.html +18 -2
- package/runtime/arcane/components/relationship-board.html +23 -3
- package/runtime/arcane/components/screen-capture.html +10 -4
- package/runtime/arcane/components/source-code-viewer.html +76 -9
- package/runtime/arcane/components/source-explanation.html +23 -3
- package/runtime/arcane/components/speech.html +462 -384
- package/runtime/arcane/components/summary-strip.html +22 -11
- package/runtime/arcane/components/table.html +39 -21
- package/runtime/arcane/components/task-progress.html +79 -21
- package/runtime/arcane/components/terminal-workspace.html +7 -4
- package/runtime/arcane/components/theme-editor.html +7 -3
- package/runtime/arcane/components/unified-inbox.html +9 -4
- package/runtime/arcane/components/voice-transcription.html +639 -98
- package/runtime/arcane/components/weather-widget.html +5 -3
- package/runtime/arcane/components/web-navigator.html +48 -8
- package/runtime/arcane/entities/Chat.js +1 -1
- package/runtime/arcane/entities/User.js +110 -23
- package/runtime/arcane/modules/AI.js +2109 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
- package/runtime/arcane/modules/AIRuntimeState.js +109 -52
- package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
- package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
- package/runtime/arcane/modules/CalculatorEngine.js +63 -3
- package/runtime/arcane/modules/CommunicationAppController.js +588 -28
- package/runtime/arcane/modules/CommunicationHub.js +590 -10
- package/runtime/arcane/modules/ComponentContracts.js +470 -0
- package/runtime/arcane/modules/ConversationTimebox.js +152 -33
- package/runtime/arcane/modules/DBLS.js +40 -7
- package/runtime/arcane/modules/DBOPFS.js +35 -11
- package/runtime/arcane/modules/DataMaintenance.js +12 -2
- package/runtime/arcane/modules/Errors.js +65 -7
- package/runtime/arcane/modules/HTMLImport.js +198 -14
- package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
- package/runtime/arcane/modules/Mail.js +738 -115
- package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
- package/runtime/arcane/modules/MailTransport.mjs +197 -39
- package/runtime/arcane/modules/Ollama.js +36 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
- package/runtime/arcane/modules/PreferenceStore.js +367 -33
- package/runtime/arcane/modules/RecordReviewStore.js +322 -23
- package/runtime/arcane/modules/ScreenCapture.js +1397 -15
- package/runtime/arcane/modules/SpeechPlayback.js +438 -41
- package/runtime/arcane/modules/TerminalClient.js +277 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
- package/runtime/arcane/modules/ThemeManager.js +39 -7
- package/runtime/arcane/modules/TimeGuard.js +131 -20
- package/runtime/arcane/modules/WaitForComponent.js +386 -33
- package/schemas/arcane-lock.schema.json +2 -2
- package/src/cli/main.mjs +435 -9
- package/src/event-manager.mjs +1097 -1
- package/src/import-map.mjs +21 -3
- package/src/index.mjs +13 -0
- package/src/installed-sdk-runtime.mjs +112 -0
- package/src/mail-api.mjs +22 -0
- package/src/mail-credentials.mjs +667 -0
- package/src/mail-server.mjs +1769 -0
- package/src/mail.mjs +261 -0
- package/src/sdk-browser-runtime.mjs +85 -41
- package/src/testing-loader.mjs +7 -0
- package/src/toolchain.mjs +3 -0
- package/src/workspace.mjs +1 -1
package/src/cli/main.mjs
CHANGED
|
@@ -22,6 +22,12 @@ const VALUE_OPTIONS=new Set([
|
|
|
22
22
|
'scope',
|
|
23
23
|
'test-file',
|
|
24
24
|
'artifact',
|
|
25
|
+
'profile',
|
|
26
|
+
'from',
|
|
27
|
+
'origin',
|
|
28
|
+
'allow-to',
|
|
29
|
+
'report-key',
|
|
30
|
+
'request-timeout',
|
|
25
31
|
'output'
|
|
26
32
|
]);
|
|
27
33
|
const FLAG_OPTIONS=new Set([
|
|
@@ -30,9 +36,17 @@ const FLAG_OPTIONS=new Set([
|
|
|
30
36
|
'dry-run',
|
|
31
37
|
'require-local-ai',
|
|
32
38
|
'overwrite',
|
|
39
|
+
'secret-stdin',
|
|
40
|
+
'app-key-stdin',
|
|
41
|
+
'report-stdin',
|
|
33
42
|
'help',
|
|
34
43
|
'version'
|
|
35
44
|
]);
|
|
45
|
+
const REPORTABLE_COMMANDS=new Set([
|
|
46
|
+
'build','bundle','check','dev','doctor','help','import-map','init','mail',
|
|
47
|
+
'native-doctor','native-prepare','new','package','repo','run','targets','test',
|
|
48
|
+
'update-check','verify','verify-bundle','version'
|
|
49
|
+
]);
|
|
36
50
|
|
|
37
51
|
export const HELP_TEXT=`Arcane OS application SDK ${SDK_VERSION}
|
|
38
52
|
|
|
@@ -57,6 +71,11 @@ Usage:
|
|
|
57
71
|
${CLI_NAME} update-check
|
|
58
72
|
${CLI_NAME} targets
|
|
59
73
|
${CLI_NAME} repo status|pull|push
|
|
74
|
+
${CLI_NAME} mail key set <profile> [--secret-stdin]
|
|
75
|
+
${CLI_NAME} mail key status <profile>
|
|
76
|
+
${CLI_NAME} mail key delete <profile>
|
|
77
|
+
${CLI_NAME} mail send --profile <profile> --from <address> --report-key <id> --report-stdin [--request-timeout <ms>]
|
|
78
|
+
${CLI_NAME} mail serve --profile <profile> --from <address> --app <id> --origin <origin> --allow-to <addresses> [--app-key-stdin] [--host 127.0.0.1] [--port 8025] [--request-timeout <ms>]
|
|
60
79
|
|
|
61
80
|
Development:
|
|
62
81
|
--sdk-runtime-source <sdk-root> Dev-only live SDK checkout; omitted preserves the workspace runtime mode.
|
|
@@ -99,7 +118,7 @@ function extractOutput(argv){
|
|
|
99
118
|
remaining.push(argument);
|
|
100
119
|
}
|
|
101
120
|
if(!OUTPUT_MODES.includes(output)){
|
|
102
|
-
usage(
|
|
121
|
+
usage('Invalid --output value. Expected human, json, or ndjson.');
|
|
103
122
|
}
|
|
104
123
|
return {output,remaining};
|
|
105
124
|
}
|
|
@@ -126,7 +145,7 @@ export function parseCliArguments(argv){
|
|
|
126
145
|
continue;
|
|
127
146
|
}
|
|
128
147
|
if(!VALUE_OPTIONS.has(name)){
|
|
129
|
-
usage(
|
|
148
|
+
usage('Unknown command-line option.');
|
|
130
149
|
}
|
|
131
150
|
const value=equal===-1?argv[++index]:argument.slice(equal+1);
|
|
132
151
|
if(value===undefined||value===''||(equal===-1&&value.startsWith('-'))){
|
|
@@ -154,6 +173,254 @@ function readPort(value,defaultValue){
|
|
|
154
173
|
return port;
|
|
155
174
|
}
|
|
156
175
|
|
|
176
|
+
function readRequestTimeout(value,defaultValue){
|
|
177
|
+
if(value===undefined){
|
|
178
|
+
return defaultValue;
|
|
179
|
+
}
|
|
180
|
+
if(!/^\d+$/u.test(value)){
|
|
181
|
+
usage(`Invalid request timeout: ${value}.`);
|
|
182
|
+
}
|
|
183
|
+
const timeout=Number(value);
|
|
184
|
+
if(!Number.isSafeInteger(timeout)||timeout<1_000||timeout>600_000){
|
|
185
|
+
usage('Mail request timeout must be an integer between 1000 and 600000 milliseconds.');
|
|
186
|
+
}
|
|
187
|
+
return timeout;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const MAX_MAIL_SECRET_BYTES=8_192;
|
|
191
|
+
const MAX_MAIL_REPORT_BYTES=52*1024*1024;
|
|
192
|
+
|
|
193
|
+
function normalizedSecret(value){
|
|
194
|
+
const secret=String(value??'').trim();
|
|
195
|
+
if(!secret||Buffer.byteLength(secret,'utf8')>MAX_MAIL_SECRET_BYTES){
|
|
196
|
+
usage(`Mail credential input must contain 1-${MAX_MAIL_SECRET_BYTES} UTF-8 bytes.`);
|
|
197
|
+
}
|
|
198
|
+
return secret;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function readPipedMailSecret(input,signal){
|
|
202
|
+
return new Promise((resolve,reject)=>{
|
|
203
|
+
const chunks=[];
|
|
204
|
+
let byteLength=0;
|
|
205
|
+
let settled=false;
|
|
206
|
+
|
|
207
|
+
const cleanup=function cleanupMailSecretRead(){
|
|
208
|
+
input.removeListener('data',onData);
|
|
209
|
+
input.removeListener('end',onEnd);
|
|
210
|
+
input.removeListener('error',onError);
|
|
211
|
+
signal?.removeEventListener('abort',onAbort);
|
|
212
|
+
};
|
|
213
|
+
const finish=function finishMailSecretRead(callback,value){
|
|
214
|
+
if(settled)return;
|
|
215
|
+
settled=true;
|
|
216
|
+
cleanup();
|
|
217
|
+
callback(value);
|
|
218
|
+
};
|
|
219
|
+
const onData=function collectMailSecretChunk(chunk){
|
|
220
|
+
const bytes=Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk);
|
|
221
|
+
byteLength+=bytes.byteLength;
|
|
222
|
+
if(byteLength>MAX_MAIL_SECRET_BYTES+2){
|
|
223
|
+
finish(reject,new ArcaneError(
|
|
224
|
+
ERROR_CODES.usage,
|
|
225
|
+
`Mail credential input cannot exceed ${MAX_MAIL_SECRET_BYTES} UTF-8 bytes.`
|
|
226
|
+
));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
chunks.push(bytes);
|
|
230
|
+
};
|
|
231
|
+
const onEnd=function finishPipedMailSecret(){
|
|
232
|
+
try{
|
|
233
|
+
finish(resolve,normalizedSecret(Buffer.concat(chunks).toString('utf8')));
|
|
234
|
+
}catch(error){
|
|
235
|
+
finish(reject,error);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const onError=function failPipedMailSecret(error){
|
|
239
|
+
finish(reject,new ArcaneError(
|
|
240
|
+
ERROR_CODES.operationFailed,
|
|
241
|
+
'Unable to read the mail credential from standard input.',
|
|
242
|
+
{cause:error}
|
|
243
|
+
));
|
|
244
|
+
};
|
|
245
|
+
const onAbort=function cancelPipedMailSecret(){
|
|
246
|
+
finish(reject,new ArcaneError(
|
|
247
|
+
ERROR_CODES.cancelled,
|
|
248
|
+
'Mail credential entry was cancelled.',
|
|
249
|
+
{cause:signal?.reason,exitCode:130}
|
|
250
|
+
));
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
input.on('data',onData);
|
|
254
|
+
input.once('end',onEnd);
|
|
255
|
+
input.once('error',onError);
|
|
256
|
+
signal?.addEventListener('abort',onAbort,{once:true});
|
|
257
|
+
if(signal?.aborted){
|
|
258
|
+
onAbort();
|
|
259
|
+
}else{
|
|
260
|
+
input.resume?.();
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function readMaskedMailSecret(input,output,signal,label,stdinOption){
|
|
266
|
+
if(!input?.isTTY||typeof input.setRawMode!=='function'||typeof output?.write!=='function'){
|
|
267
|
+
usage(`Interactive mail credential entry requires a terminal; use ${stdinOption} for piped input.`);
|
|
268
|
+
}
|
|
269
|
+
return new Promise((resolve,reject)=>{
|
|
270
|
+
let secret='';
|
|
271
|
+
let settled=false;
|
|
272
|
+
const priorRaw=Boolean(input.isRaw);
|
|
273
|
+
|
|
274
|
+
const cleanup=function cleanupMaskedMailSecret(){
|
|
275
|
+
input.removeListener('data',onData);
|
|
276
|
+
signal?.removeEventListener('abort',onAbort);
|
|
277
|
+
input.setRawMode(priorRaw);
|
|
278
|
+
if(!priorRaw)input.pause?.();
|
|
279
|
+
};
|
|
280
|
+
const finish=function finishMaskedMailSecret(callback,value){
|
|
281
|
+
if(settled)return;
|
|
282
|
+
settled=true;
|
|
283
|
+
cleanup();
|
|
284
|
+
output.write('\n');
|
|
285
|
+
callback(value);
|
|
286
|
+
};
|
|
287
|
+
const onAbort=function cancelMaskedMailSecret(){
|
|
288
|
+
finish(reject,new ArcaneError(
|
|
289
|
+
ERROR_CODES.cancelled,
|
|
290
|
+
'Mail credential entry was cancelled.',
|
|
291
|
+
{cause:signal?.reason,exitCode:130}
|
|
292
|
+
));
|
|
293
|
+
};
|
|
294
|
+
const onData=function collectMaskedMailSecret(chunk){
|
|
295
|
+
for(const character of String(chunk)){
|
|
296
|
+
if(character==='\u0003'){
|
|
297
|
+
onAbort();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if(character==='\r'||character==='\n'){
|
|
301
|
+
try{
|
|
302
|
+
finish(resolve,normalizedSecret(secret));
|
|
303
|
+
}catch(error){
|
|
304
|
+
finish(reject,error);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if(character==='\b'||character==='\u007f'){
|
|
309
|
+
secret=Array.from(secret).slice(0,-1).join('');
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if(character>=' '&&character!=='\u007f'){
|
|
313
|
+
const candidate=secret+character;
|
|
314
|
+
if(Buffer.byteLength(candidate,'utf8')<=MAX_MAIL_SECRET_BYTES){
|
|
315
|
+
secret=candidate;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
output.write(`${label} (input hidden): `);
|
|
322
|
+
input.setRawMode(true);
|
|
323
|
+
input.on('data',onData);
|
|
324
|
+
signal?.addEventListener('abort',onAbort,{once:true});
|
|
325
|
+
input.resume();
|
|
326
|
+
if(signal?.aborted)onAbort();
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function readMailSecretInput({input=process.stdin,output=process.stderr,
|
|
331
|
+
secretStdin=false,signal,label='Resend API key',stdinOption='--secret-stdin'}={}){
|
|
332
|
+
if(secretStdin&&input?.isTTY){
|
|
333
|
+
usage(`${stdinOption} requires redirected or piped input; omit it for hidden interactive entry.`);
|
|
334
|
+
}
|
|
335
|
+
return secretStdin
|
|
336
|
+
? readPipedMailSecret(input,signal)
|
|
337
|
+
: readMaskedMailSecret(input,output,signal,label,stdinOption);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function readPipedMailReport(input,signal){
|
|
341
|
+
return new Promise((resolve,reject)=>{
|
|
342
|
+
const chunks=[];
|
|
343
|
+
let byteLength=0;
|
|
344
|
+
let settled=false;
|
|
345
|
+
|
|
346
|
+
const cleanup=function cleanupMailReportRead(){
|
|
347
|
+
input.removeListener('data',onData);
|
|
348
|
+
input.removeListener('end',onEnd);
|
|
349
|
+
input.removeListener('error',onError);
|
|
350
|
+
signal?.removeEventListener('abort',onAbort);
|
|
351
|
+
};
|
|
352
|
+
const finish=function finishMailReportRead(callback,value){
|
|
353
|
+
if(settled)return;
|
|
354
|
+
settled=true;
|
|
355
|
+
cleanup();
|
|
356
|
+
callback(value);
|
|
357
|
+
};
|
|
358
|
+
const onData=function collectMailReportChunk(chunk){
|
|
359
|
+
const bytes=Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk);
|
|
360
|
+
byteLength+=bytes.byteLength;
|
|
361
|
+
if(byteLength>MAX_MAIL_REPORT_BYTES){
|
|
362
|
+
finish(reject,new ArcaneError(
|
|
363
|
+
ERROR_CODES.usage,
|
|
364
|
+
`Mail report input cannot exceed ${MAX_MAIL_REPORT_BYTES} UTF-8 bytes.`
|
|
365
|
+
));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
chunks.push(bytes);
|
|
369
|
+
};
|
|
370
|
+
const onEnd=function finishPipedMailReport(){
|
|
371
|
+
try{
|
|
372
|
+
const serialized=Buffer.concat(chunks).toString('utf8');
|
|
373
|
+
let report;
|
|
374
|
+
try{
|
|
375
|
+
report=JSON.parse(serialized);
|
|
376
|
+
}catch{
|
|
377
|
+
usage('Mail report input must be one valid JSON object.');
|
|
378
|
+
}
|
|
379
|
+
if(!report||typeof report!=='object'||Array.isArray(report)){
|
|
380
|
+
usage('Mail report input must be one valid JSON object.');
|
|
381
|
+
}
|
|
382
|
+
finish(resolve,report);
|
|
383
|
+
}catch(error){
|
|
384
|
+
finish(reject,error);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
const onError=function failPipedMailReport(error){
|
|
388
|
+
finish(reject,new ArcaneError(
|
|
389
|
+
ERROR_CODES.operationFailed,
|
|
390
|
+
'Unable to read the mail report from standard input.',
|
|
391
|
+
{cause:error}
|
|
392
|
+
));
|
|
393
|
+
};
|
|
394
|
+
const onAbort=function cancelPipedMailReport(){
|
|
395
|
+
finish(reject,new ArcaneError(
|
|
396
|
+
ERROR_CODES.cancelled,
|
|
397
|
+
'Mail report input was cancelled.',
|
|
398
|
+
{cause:signal?.reason,exitCode:130}
|
|
399
|
+
));
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
input.on('data',onData);
|
|
403
|
+
input.once('end',onEnd);
|
|
404
|
+
input.once('error',onError);
|
|
405
|
+
signal?.addEventListener('abort',onAbort,{once:true});
|
|
406
|
+
if(signal?.aborted){
|
|
407
|
+
onAbort();
|
|
408
|
+
}else{
|
|
409
|
+
input.resume?.();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function readMailReportInput({input=process.stdin,reportStdin=false,signal}={}){
|
|
415
|
+
if(!reportStdin){
|
|
416
|
+
usage('mail send requires --report-stdin.');
|
|
417
|
+
}
|
|
418
|
+
if(input?.isTTY){
|
|
419
|
+
usage('--report-stdin requires redirected or piped input.');
|
|
420
|
+
}
|
|
421
|
+
return readPipedMailReport(input,signal);
|
|
422
|
+
}
|
|
423
|
+
|
|
157
424
|
function readScope(value){
|
|
158
425
|
const scope=value??'app';
|
|
159
426
|
if(!['app','shared'].includes(scope)){
|
|
@@ -173,7 +440,7 @@ function inferredAppId(workspaceRoot){
|
|
|
173
440
|
|
|
174
441
|
function noExtraPositionals(command,positionals,expected=0){
|
|
175
442
|
if(positionals.length>expected){
|
|
176
|
-
usage(`Unexpected argument for ${command}
|
|
443
|
+
usage(`Unexpected argument for ${command}.`);
|
|
177
444
|
}
|
|
178
445
|
}
|
|
179
446
|
|
|
@@ -184,7 +451,10 @@ function operationOptions(command,parsed,cwd){
|
|
|
184
451
|
const scope=readScope(values.scope);
|
|
185
452
|
const common={workspaceRoot,appId:values.app,scope};
|
|
186
453
|
if(scope==='shared'&&!['test','check'].includes(command)){
|
|
187
|
-
usage(
|
|
454
|
+
usage(
|
|
455
|
+
`--scope shared is not supported by ${reportableCommand(command)}; `
|
|
456
|
+
+'shared development cannot package or build app output.'
|
|
457
|
+
);
|
|
188
458
|
}
|
|
189
459
|
if(values['test-file']!==undefined&&command!=='test'){
|
|
190
460
|
usage('--test-file is supported only by test --scope shared.');
|
|
@@ -198,6 +468,19 @@ function operationOptions(command,parsed,cwd){
|
|
|
198
468
|
if(flags.has('overwrite')&&command!=='bundle'){
|
|
199
469
|
usage('--overwrite is supported only by bundle.');
|
|
200
470
|
}
|
|
471
|
+
const mailOnlyOptions=['profile','from','origin','allow-to','report-key','request-timeout'];
|
|
472
|
+
if(command!=='mail'&&mailOnlyOptions.some(name=>values[name]!==undefined)){
|
|
473
|
+
usage(`Mail options are supported only by the mail command.`);
|
|
474
|
+
}
|
|
475
|
+
if(command!=='mail'&&flags.has('secret-stdin')){
|
|
476
|
+
usage('--secret-stdin is supported only by mail key set.');
|
|
477
|
+
}
|
|
478
|
+
if(command!=='mail'&&flags.has('app-key-stdin')){
|
|
479
|
+
usage('--app-key-stdin is supported only by mail serve.');
|
|
480
|
+
}
|
|
481
|
+
if(command!=='mail'&&flags.has('report-stdin')){
|
|
482
|
+
usage('--report-stdin is supported only by mail send.');
|
|
483
|
+
}
|
|
201
484
|
|
|
202
485
|
if(command==='new'){
|
|
203
486
|
const appId=positionals[0];
|
|
@@ -342,7 +625,98 @@ function operationOptions(command,parsed,cwd){
|
|
|
342
625
|
}
|
|
343
626
|
return {...common,action};
|
|
344
627
|
}
|
|
345
|
-
|
|
628
|
+
if(command==='mail'){
|
|
629
|
+
const area=positionals[0];
|
|
630
|
+
if(area==='key'){
|
|
631
|
+
noExtraPositionals(command,positionals,3);
|
|
632
|
+
const action=positionals[1];
|
|
633
|
+
const profile=positionals[2];
|
|
634
|
+
if(!['set','status','delete'].includes(action)||!profile){
|
|
635
|
+
usage('mail key requires set, status, or delete followed by one profile id.');
|
|
636
|
+
}
|
|
637
|
+
if(values.profile!==undefined||values.from!==undefined||values.app!==undefined
|
|
638
|
+
||values.origin!==undefined
|
|
639
|
+
||values['allow-to']!==undefined||values.host!==undefined||values.port!==undefined
|
|
640
|
+
||values['report-key']!==undefined||values['request-timeout']!==undefined){
|
|
641
|
+
usage('mail key accepts only its profile argument and optional --secret-stdin for set.');
|
|
642
|
+
}
|
|
643
|
+
if(flags.has('secret-stdin')&&action!=='set'){
|
|
644
|
+
usage('--secret-stdin is supported only by mail key set.');
|
|
645
|
+
}
|
|
646
|
+
if(flags.has('app-key-stdin')){
|
|
647
|
+
usage('--app-key-stdin is supported only by mail serve.');
|
|
648
|
+
}
|
|
649
|
+
if(flags.has('report-stdin')){
|
|
650
|
+
usage('--report-stdin is supported only by mail send.');
|
|
651
|
+
}
|
|
652
|
+
return {
|
|
653
|
+
action:`key-${action}`,
|
|
654
|
+
profile,
|
|
655
|
+
secretStdin:flags.has('secret-stdin'),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
if(area==='serve'){
|
|
659
|
+
noExtraPositionals(command,positionals,1);
|
|
660
|
+
if(flags.has('secret-stdin')){
|
|
661
|
+
usage('--secret-stdin is not supported by mail serve.');
|
|
662
|
+
}
|
|
663
|
+
if(flags.has('report-stdin')||values['report-key']!==undefined){
|
|
664
|
+
usage('--report-stdin and --report-key are supported only by mail send.');
|
|
665
|
+
}
|
|
666
|
+
for(const [name,value]of Object.entries({
|
|
667
|
+
profile:values.profile,
|
|
668
|
+
from:values.from,
|
|
669
|
+
app:values.app,
|
|
670
|
+
origin:values.origin,
|
|
671
|
+
'allow-to':values['allow-to'],
|
|
672
|
+
})){
|
|
673
|
+
if(!value)usage(`mail serve requires --${name} <value>.`);
|
|
674
|
+
}
|
|
675
|
+
return {
|
|
676
|
+
action:'serve',
|
|
677
|
+
profile:values.profile,
|
|
678
|
+
from:values.from,
|
|
679
|
+
appId:values.app,
|
|
680
|
+
origin:values.origin,
|
|
681
|
+
allowTo:values['allow-to'],
|
|
682
|
+
appKeyStdin:flags.has('app-key-stdin'),
|
|
683
|
+
host:values.host??'127.0.0.1',
|
|
684
|
+
port:readPort(values.port,8025),
|
|
685
|
+
requestTimeout:readRequestTimeout(values['request-timeout'],30_000),
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
if(area==='send'){
|
|
689
|
+
noExtraPositionals(command,positionals,1);
|
|
690
|
+
if(flags.has('secret-stdin')||flags.has('app-key-stdin')){
|
|
691
|
+
usage('mail send accepts report input only through --report-stdin.');
|
|
692
|
+
}
|
|
693
|
+
if(values.app!==undefined||values.origin!==undefined
|
|
694
|
+
||values['allow-to']!==undefined||values.host!==undefined
|
|
695
|
+
||values.port!==undefined){
|
|
696
|
+
usage('mail send does not accept gateway server options.');
|
|
697
|
+
}
|
|
698
|
+
for(const [name,value]of Object.entries({
|
|
699
|
+
profile:values.profile,
|
|
700
|
+
from:values.from,
|
|
701
|
+
'report-key':values['report-key'],
|
|
702
|
+
})){
|
|
703
|
+
if(!value)usage(`mail send requires --${name} <value>.`);
|
|
704
|
+
}
|
|
705
|
+
if(!flags.has('report-stdin')){
|
|
706
|
+
usage('mail send requires --report-stdin.');
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
action:'send',
|
|
710
|
+
profile:values.profile,
|
|
711
|
+
from:values.from,
|
|
712
|
+
reportKey:values['report-key'],
|
|
713
|
+
reportStdin:true,
|
|
714
|
+
requestTimeout:readRequestTimeout(values['request-timeout'],30_000),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
usage('mail requires key set|status|delete <profile>, send, or serve.');
|
|
718
|
+
}
|
|
719
|
+
usage(`Unknown command. Run ${CLI_NAME} --help for usage.`);
|
|
346
720
|
}
|
|
347
721
|
|
|
348
722
|
const NATIVE_REQUESTS=Object.freeze({
|
|
@@ -460,6 +834,10 @@ function commandFromArgs(args){
|
|
|
460
834
|
return 'help';
|
|
461
835
|
}
|
|
462
836
|
|
|
837
|
+
function reportableCommand(command){
|
|
838
|
+
return REPORTABLE_COMMANDS.has(command)?command:'unknown';
|
|
839
|
+
}
|
|
840
|
+
|
|
463
841
|
function serverSummary(result){
|
|
464
842
|
return {
|
|
465
843
|
target:result.target??'browser',
|
|
@@ -468,6 +846,9 @@ function serverSummary(result){
|
|
|
468
846
|
host:result.host,
|
|
469
847
|
port:result.port,
|
|
470
848
|
url:result.url,
|
|
849
|
+
...(result.callerAuthentication
|
|
850
|
+
?{callerAuthentication:result.callerAuthentication}
|
|
851
|
+
:{}),
|
|
471
852
|
...(result.runtimeMode?{runtimeMode:result.runtimeMode}:{}),
|
|
472
853
|
...(result.runtime?{runtime:result.runtime}:{}),
|
|
473
854
|
...(result.verified?{verified:result.verified}:{})
|
|
@@ -517,6 +898,7 @@ async function waitForServer(result,signal,reporter){
|
|
|
517
898
|
|
|
518
899
|
export async function runCli(argv=process.argv.slice(2),{
|
|
519
900
|
cwd=process.cwd(),
|
|
901
|
+
stdin=process.stdin,
|
|
520
902
|
stdout=process.stdout,
|
|
521
903
|
stderr=process.stderr,
|
|
522
904
|
execute=executeOperation,
|
|
@@ -529,8 +911,13 @@ export async function runCli(argv=process.argv.slice(2),{
|
|
|
529
911
|
try{
|
|
530
912
|
const extracted=extractOutput([...argv]);
|
|
531
913
|
command=commandFromArgs(extracted.remaining);
|
|
532
|
-
reporter=createReporter({
|
|
533
|
-
|
|
914
|
+
reporter=createReporter({
|
|
915
|
+
command:reportableCommand(command),
|
|
916
|
+
output:extracted.output,
|
|
917
|
+
stdout,
|
|
918
|
+
stderr
|
|
919
|
+
});
|
|
920
|
+
reporter.accept();
|
|
534
921
|
process.once('SIGINT',onSignal);
|
|
535
922
|
process.once('SIGTERM',onSignal);
|
|
536
923
|
|
|
@@ -544,9 +931,47 @@ export async function runCli(argv=process.argv.slice(2),{
|
|
|
544
931
|
return 0;
|
|
545
932
|
}
|
|
546
933
|
|
|
934
|
+
const operation=operationOptions(command,parsed,cwd);
|
|
935
|
+
if(command==='mail'&&operation.action==='key-set'){
|
|
936
|
+
operation.readSecret=function readMailCredentialForOperation(){
|
|
937
|
+
if(reporter.output!=='human'&&!operation.secretStdin){
|
|
938
|
+
usage('Structured output requires mail key set --secret-stdin.');
|
|
939
|
+
}
|
|
940
|
+
return readMailSecretInput({
|
|
941
|
+
input:stdin,
|
|
942
|
+
output:stderr,
|
|
943
|
+
secretStdin:operation.secretStdin,
|
|
944
|
+
signal:controller.signal,
|
|
945
|
+
});
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if(command==='mail'&&operation.action==='serve'){
|
|
949
|
+
operation.readAppKey=function readMailGatewayAppKeyForOperation(){
|
|
950
|
+
if(reporter.output!=='human'&&!operation.appKeyStdin){
|
|
951
|
+
usage('Structured output requires mail serve --app-key-stdin.');
|
|
952
|
+
}
|
|
953
|
+
return readMailSecretInput({
|
|
954
|
+
input:stdin,
|
|
955
|
+
output:stderr,
|
|
956
|
+
secretStdin:operation.appKeyStdin,
|
|
957
|
+
signal:controller.signal,
|
|
958
|
+
label:'Mail gateway app key',
|
|
959
|
+
stdinOption:'--app-key-stdin',
|
|
960
|
+
});
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
if(command==='mail'&&operation.action==='send'){
|
|
964
|
+
operation.readReport=function readMailReportForOperation(){
|
|
965
|
+
return readMailReportInput({
|
|
966
|
+
input:stdin,
|
|
967
|
+
reportStdin:operation.reportStdin,
|
|
968
|
+
signal:controller.signal,
|
|
969
|
+
});
|
|
970
|
+
};
|
|
971
|
+
}
|
|
547
972
|
const options=await pairNativeProvider(
|
|
548
973
|
command,
|
|
549
|
-
|
|
974
|
+
operation,
|
|
550
975
|
loadNativeProvider,
|
|
551
976
|
{signal:controller.signal,onEvent:event=>reporter.forward(event)}
|
|
552
977
|
);
|
|
@@ -555,7 +980,8 @@ export async function runCli(argv=process.argv.slice(2),{
|
|
|
555
980
|
signal:controller.signal,
|
|
556
981
|
onEvent:event=>reporter.forward(event)
|
|
557
982
|
});
|
|
558
|
-
const finalResult=(command==='dev'||(command==='run'&&(options.target??'browser')==='browser')
|
|
983
|
+
const finalResult=(command==='dev'||(command==='run'&&(options.target??'browser')==='browser')
|
|
984
|
+
||(command==='mail'&&options.action==='serve'))
|
|
559
985
|
?await waitForServer(result,controller.signal,reporter)
|
|
560
986
|
:result;
|
|
561
987
|
reporter.complete(finalResult);
|