orizu 0.5.49 → 0.5.50

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.
Files changed (74) hide show
  1. package/dist/account-directory.d.ts +2 -2
  2. package/dist/account-directory.d.ts.map +1 -1
  3. package/dist/account-directory.js +7 -32
  4. package/dist/auth-commands.d.ts.map +1 -1
  5. package/dist/auth-commands.js +22 -30
  6. package/dist/credentials.d.ts +5 -0
  7. package/dist/credentials.d.ts.map +1 -1
  8. package/dist/credentials.js +84 -3
  9. package/dist/dataset-transfer-upload.d.ts.map +1 -1
  10. package/dist/dataset-transfer-upload.js +6 -1
  11. package/dist/directory-list-read.d.ts +37 -0
  12. package/dist/directory-list-read.d.ts.map +1 -0
  13. package/dist/directory-list-read.js +87 -0
  14. package/dist/hosted-boot.d.ts.map +1 -1
  15. package/dist/hosted-boot.js +59 -41
  16. package/dist/hosted-harness-opencode.d.ts +31 -0
  17. package/dist/hosted-harness-opencode.d.ts.map +1 -1
  18. package/dist/hosted-harness-opencode.js +45 -7
  19. package/dist/hosted-loop.d.ts +39 -2
  20. package/dist/hosted-loop.d.ts.map +1 -1
  21. package/dist/hosted-loop.js +127 -18
  22. package/dist/hosted-session-seed.d.ts +2 -0
  23. package/dist/hosted-session-seed.d.ts.map +1 -1
  24. package/dist/hosted-session-seed.js +27 -10
  25. package/dist/http.d.ts.map +1 -1
  26. package/dist/http.js +39 -234
  27. package/dist/keyboard-select.d.ts.map +1 -1
  28. package/dist/keyboard-select.js +23 -45
  29. package/dist/project-create-workflow.d.ts +18 -0
  30. package/dist/project-create-workflow.d.ts.map +1 -0
  31. package/dist/project-create-workflow.js +121 -0
  32. package/dist/self-update-command.d.ts +8 -0
  33. package/dist/self-update-command.d.ts.map +1 -0
  34. package/dist/self-update-command.js +390 -0
  35. package/dist/self-update.d.ts +29 -16
  36. package/dist/self-update.d.ts.map +1 -1
  37. package/dist/self-update.js +55 -244
  38. package/dist/setup-account.d.ts.map +1 -1
  39. package/dist/setup-account.js +4 -1
  40. package/dist/setup-and-skills-commands.d.ts.map +1 -1
  41. package/dist/setup-and-skills-commands.js +88 -102
  42. package/dist/skill-install-workflow.d.ts +23 -0
  43. package/dist/skill-install-workflow.d.ts.map +1 -0
  44. package/dist/skill-install-workflow.js +184 -0
  45. package/dist/skill-installer.d.ts +8 -3
  46. package/dist/skill-installer.d.ts.map +1 -1
  47. package/dist/skill-installer.js +26 -33
  48. package/dist/stored-session-requests.d.ts +27 -0
  49. package/dist/stored-session-requests.d.ts.map +1 -0
  50. package/dist/stored-session-requests.js +264 -0
  51. package/dist/terminal-key-session.d.ts +23 -0
  52. package/dist/terminal-key-session.d.ts.map +1 -0
  53. package/dist/terminal-key-session.js +74 -0
  54. package/package.json +1 -1
  55. package/src/account-directory.ts +8 -42
  56. package/src/auth-commands.ts +20 -29
  57. package/src/credentials.ts +83 -3
  58. package/src/dataset-transfer-upload.ts +7 -1
  59. package/src/directory-list-read.ts +123 -0
  60. package/src/hosted-boot.ts +65 -43
  61. package/src/hosted-harness-opencode.ts +65 -9
  62. package/src/hosted-loop.ts +152 -18
  63. package/src/hosted-session-seed.ts +25 -8
  64. package/src/http.ts +39 -317
  65. package/src/keyboard-select.ts +18 -55
  66. package/src/project-create-workflow.ts +139 -0
  67. package/src/self-update-command.ts +419 -0
  68. package/src/self-update.ts +70 -266
  69. package/src/setup-account.ts +4 -1
  70. package/src/setup-and-skills-commands.ts +89 -121
  71. package/src/skill-install-workflow.ts +215 -0
  72. package/src/skill-installer.ts +31 -38
  73. package/src/stored-session-requests.ts +316 -0
  74. package/src/terminal-key-session.ts +88 -0
@@ -1,13 +1,13 @@
1
1
  // ORI-2044: moved from index.ts to keep setup and skills commands together.
2
2
  import { createInterface } from 'readline/promises';
3
3
  import { stdin as input, stdout as output } from 'process';
4
- import { emitKeypressEvents } from 'readline';
5
4
  import { join, relative, resolve } from 'path';
6
5
  import { mkdirSync, writeFileSync } from 'fs';
7
6
  import { randomBytes } from 'crypto';
8
7
  import { spawnSync } from 'child_process';
9
8
  import { isInteractiveTerminal, getArg, getArgs, hasJsonFlag, hasArg, normalizeSlugInput, getOptionalArgValue, expandHomePath, cliArgs, } from './command-line-arguments.js';
10
9
  import { printLine, getCliVersion, getErrorMessage, printJson, printBannerIfInteractive, } from './cli-console-output.js';
10
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
11
11
  import { sanitizeTerminalText } from './json-response.js';
12
12
  import { SKILL_INSTALL_AGENTS, SKILL_INSTALL_TARGETS, getTargetForAgent, getSkillInstallPath, isSkillInstallAgent, isSkillInstallTarget, installSkillTarget, targetNeedsOverwrite, resolveSkillSource, getSkillTargetStatus, computeSkillContentHash, findLegacySkillInstalls, replaceLegacySkillInstalls, PROJECT_SKILL_COPY_TARGETS, compareProjectSkillCopies, withProjectSkillCopyPair, } from './skill-installer.js';
13
13
  import { createTeamOnServer, fetchTeams, fetchProjects, createProjectOnServer, } from './account-directory.js';
@@ -49,30 +49,13 @@ async function waitForAnyKeyOrEscape(message) {
49
49
  throw new Error('Interactive terminal required.');
50
50
  }
51
51
  printLine(message);
52
- const rawInput = input;
53
- const wasRaw = Boolean(rawInput.isRaw);
54
- emitKeypressEvents(input);
55
- return new Promise((resolvePromise, rejectPromise) => {
56
- function cleanup() {
57
- input.off('keypress', onKeypress);
58
- if (rawInput.setRawMode && !wasRaw) {
59
- rawInput.setRawMode(false);
60
- }
61
- input.pause();
62
- printLine('');
63
- }
64
- function onKeypress(_chunk, key) {
65
- cleanup();
66
- if (key.ctrl && key.name === 'c') {
67
- rejectPromise(new Error('Setup cancelled.'));
68
- return;
69
- }
70
- resolvePromise(key.name !== 'escape');
71
- }
72
- input.on('keypress', onKeypress);
73
- input.resume();
74
- rawInput.setRawMode?.(true);
75
- });
52
+ const { readKeys } = await import('./terminal-key-session.js');
53
+ try {
54
+ return await readKeys(key => ({ kind: 'done', value: key.name !== 'escape' }), { hideCursor: false });
55
+ }
56
+ finally {
57
+ printLine('');
58
+ }
76
59
  }
77
60
  async function promptKeyboardMultiSelect(title, items, label, defaultSelected, options = {}) {
78
61
  if (items.length === 0) {
@@ -91,9 +74,6 @@ async function promptKeyboardMultiSelect(title, items, label, defaultSelected, o
91
74
  checked.add(index);
92
75
  }
93
76
  });
94
- const rawInput = input;
95
- const wasRaw = Boolean(rawInput.isRaw);
96
- emitKeypressEvents(input);
97
77
  function render() {
98
78
  if (rendered) {
99
79
  output.write(`\x1b[${items.length}A`);
@@ -107,58 +87,34 @@ async function promptKeyboardMultiSelect(title, items, label, defaultSelected, o
107
87
  rendered = true;
108
88
  }
109
89
  render();
110
- output.write('\x1b[?25l');
111
- return new Promise((resolvePromise, rejectPromise) => {
112
- function cleanup() {
113
- input.off('keypress', onKeypress);
114
- if (rawInput.setRawMode && !wasRaw) {
115
- rawInput.setRawMode(false);
116
- }
117
- input.pause();
118
- output.write('\x1b[?25h');
119
- printLine('');
120
- }
121
- function onKeypress(_chunk, key) {
122
- if (key.ctrl && key.name === 'c') {
123
- cleanup();
124
- rejectPromise(new Error('Setup cancelled.'));
125
- return;
126
- }
90
+ const { readKeys } = await import('./terminal-key-session.js');
91
+ try {
92
+ return await readKeys(key => {
127
93
  if (key.name === 'escape') {
128
- cleanup();
129
- resolvePromise(options.escapeKeepsDefaults ? items.filter((item, index) => defaultSelected(item, index)) : []);
130
- return;
131
- }
132
- if (key.name === 'up') {
133
- selected = (selected - 1 + items.length) % items.length;
134
- render();
135
- return;
94
+ return { kind: 'done', value: options.escapeKeepsDefaults ? items.filter((item, index) => defaultSelected(item, index)) : [] };
136
95
  }
137
- if (key.name === 'down') {
138
- selected = (selected + 1) % items.length;
96
+ if (key.name === 'up' || key.name === 'down') {
97
+ selected = (selected + (key.name === 'up' ? -1 : 1) + items.length) % items.length;
139
98
  render();
140
- return;
99
+ return { kind: 'wait' };
141
100
  }
142
101
  if (key.name === 'space') {
143
- if (checked.has(selected)) {
102
+ if (checked.has(selected))
144
103
  checked.delete(selected);
145
- }
146
- else {
104
+ else
147
105
  checked.add(selected);
148
- }
149
106
  render();
150
- return;
107
+ return { kind: 'wait' };
151
108
  }
152
109
  if (key.name === 'return' || key.name === 'enter') {
153
- const selectedItems = items.filter((_item, index) => checked.has(index));
154
- cleanup();
155
- resolvePromise(selectedItems);
110
+ return { kind: 'done', value: items.filter((_item, index) => checked.has(index)) };
156
111
  }
157
- }
158
- input.on('keypress', onKeypress);
159
- input.resume();
160
- rawInput.setRawMode?.(true);
161
- });
112
+ return { kind: 'wait' };
113
+ }, { hideCursor: true });
114
+ }
115
+ finally {
116
+ printLine('');
117
+ }
162
118
  }
163
119
  function formatFindingCount(count, label) {
164
120
  return `${count} ${label}${count === 1 ? '' : 's'}`;
@@ -332,13 +288,13 @@ function describePlannedWrite(target, action, mode, path) {
332
288
  async function applySkillInstallTargets(targets, options) {
333
289
  const cliVersion = getCliVersion();
334
290
  const jsonMode = hasJsonFlag();
335
- const planned = targets.map(target => installSkillTarget(target, {
291
+ const planned = await Promise.all(targets.map(target => installSkillTarget(target, {
336
292
  cwd: options.cwd,
337
293
  homeDir: options.homeDir,
338
294
  dryRun: true,
339
295
  mode: options.mode,
340
296
  cliVersion,
341
- }));
297
+ })));
342
298
  const showPlan = options.showPlan ?? true;
343
299
  const printResults = options.printResults ?? true;
344
300
  if (showPlan && !jsonMode) {
@@ -377,7 +333,7 @@ async function applySkillInstallTargets(targets, options) {
377
333
  }
378
334
  }
379
335
  try {
380
- const result = installSkillTarget(target, {
336
+ const result = await installSkillTarget(target, {
381
337
  cwd: options.cwd,
382
338
  homeDir: options.homeDir,
383
339
  overwrite,
@@ -386,13 +342,17 @@ async function applySkillInstallTargets(targets, options) {
386
342
  });
387
343
  if (printResults && !jsonMode) {
388
344
  const suffix = result.mode === 'link' ? ' (symlink)' : '';
389
- printLine(`${formatSkillInstallAction(result.action)} ${result.path}${suffix}`);
345
+ printLine(`${formatSkillInstallAction(result.action)} ${result.path}${suffix}${describeBackup(result.backup)}`);
346
+ if (result.warning)
347
+ printLine(sanitizeSetupHumanInlineText(`Warning: ${result.warning}`));
390
348
  }
391
349
  outcomes.push({
392
350
  target,
393
351
  path: result.path,
394
352
  action: result.action,
395
353
  mode: result.mode,
354
+ ...(result.backup ? { backup: result.backup } : {}),
355
+ ...(result.warning ? { warning: result.warning } : {}),
396
356
  });
397
357
  }
398
358
  catch (error) {
@@ -405,6 +365,9 @@ async function applySkillInstallTargets(targets, options) {
405
365
  }
406
366
  return outcomes;
407
367
  }
368
+ function describeBackup(backup) {
369
+ return backup ? `; moved your earlier folder to ${backup}` : '';
370
+ }
408
371
  async function installSkillCommand() {
409
372
  const skipConfirm = hasArg('--yes');
410
373
  const dryRun = hasArg('--dry-run');
@@ -424,14 +387,14 @@ async function installSkillCommand() {
424
387
  return;
425
388
  }
426
389
  const outcomes = await applySkillInstallTargets(targets, { mode, skipConfirm, dryRun });
427
- const failedCopy = outcomes.some(outcome => outcome.action === 'failed' && PROJECT_SKILL_COPY_TARGETS.includes(outcome.target));
428
- if (failedCopy) {
390
+ if (outcomes.some(outcome => outcome.action === 'failed')) {
429
391
  process.exitCode = 1;
430
- if (!hasJsonFlag()) {
431
- printLine('The two project copies were not both written. Fix the error above and run the command again.');
432
- }
433
392
  }
434
- const legacy = replaceLegacySkillInstalls({
393
+ const failedCopy = outcomes.some(outcome => outcome.action === 'failed' && PROJECT_SKILL_COPY_TARGETS.includes(outcome.target));
394
+ if (failedCopy && !hasJsonFlag()) {
395
+ printLine('The two project copies were not both written. Fix the error above and run the command again.');
396
+ }
397
+ const legacy = await replaceLegacySkillInstalls({
435
398
  ...legacyScopeAfterInstall(targets, outcomes, dryRun),
436
399
  mode,
437
400
  dryRun,
@@ -579,7 +542,18 @@ async function skillsUpdateCommand() {
579
542
  const dryRun = hasArg('--dry-run');
580
543
  const cliVersion = getCliVersion();
581
544
  const updates = [];
582
- const legacy = replaceLegacySkillInstalls({ dryRun, cliVersion });
545
+ // A failed target is reported with its reason; the old install there stays whole.
546
+ const refresh = async (target, path, options, action) => {
547
+ try {
548
+ const result = await installSkillTarget(target, options);
549
+ updates.push({ target, path, action: action(result), ...(result.backup ? { backup: result.backup } : {}), ...(result.warning ? { warning: result.warning } : {}) });
550
+ }
551
+ catch (error) {
552
+ process.exitCode = 1;
553
+ updates.push({ target, path, action: 'failed', error: getErrorMessage(error) });
554
+ }
555
+ };
556
+ const legacy = await replaceLegacySkillInstalls({ dryRun, cliVersion });
583
557
  // Two project copies that differ are both rewritten from the installed CLI
584
558
  // skill, including a hand-made copy the loop below would leave alone.
585
559
  const projectCopies = compareProjectSkillCopies();
@@ -591,8 +565,7 @@ async function skillsUpdateCommand() {
591
565
  updates.push({ target, path, action: 'would-rewrite' });
592
566
  continue;
593
567
  }
594
- installSkillTarget(target, { overwrite: true, mode: 'copy', cliVersion });
595
- updates.push({ target, path, action: 'rewrote' });
568
+ await refresh(target, path, { overwrite: true, mode: 'copy', cliVersion }, () => 'rewrote');
596
569
  }
597
570
  }
598
571
  for (const target of SKILL_INSTALL_TARGETS) {
@@ -612,16 +585,7 @@ async function skillsUpdateCommand() {
612
585
  continue;
613
586
  }
614
587
  const refreshMode = status.mode === 'link' ? 'auto' : 'copy';
615
- const result = installSkillTarget(target, {
616
- overwrite: true,
617
- mode: refreshMode,
618
- cliVersion,
619
- });
620
- updates.push({
621
- target,
622
- path: status.path,
623
- action: status.state === 'broken-link' ? 'relinked' : result.action,
624
- });
588
+ await refresh(target, status.path, { overwrite: true, mode: refreshMode, cliVersion }, result => status.state === 'broken-link' ? 'relinked' : result.action);
625
589
  }
626
590
  if (hasJsonFlag()) {
627
591
  printJson({ updates, legacy: legacy.replacements, legacyProblems: legacy.problems });
@@ -633,14 +597,30 @@ async function skillsUpdateCommand() {
633
597
  return;
634
598
  }
635
599
  if (rewriteProjectCopies) {
636
- const verb = dryRun ? 'Would rewrite' : 'Rewrote';
637
- printLine(`${verb} both project copies from the installed CLI skill: ${projectCopies.paths.join(' and ')}`);
600
+ const rewrote = updates.filter(update => update.action === 'rewrote');
601
+ if (dryRun) {
602
+ printLine(`Would rewrite both project copies from the installed CLI skill: ${projectCopies.paths.join(' and ')}`);
603
+ }
604
+ else if (rewrote.length === PROJECT_SKILL_COPY_TARGETS.length) {
605
+ printLine(`Rewrote both project copies from the installed CLI skill: ${projectCopies.paths.join(' and ')}`);
606
+ }
638
607
  }
639
608
  for (const update of updates) {
640
- if (update.action === 'rewrote' || update.action === 'would-rewrite') {
609
+ if (update.action === 'failed') {
610
+ printLine(sanitizeSetupHumanInlineText(`Failed ${update.path}: ${update.error}`));
611
+ continue;
612
+ }
613
+ if (update.action === 'would-rewrite') {
641
614
  continue;
642
615
  }
643
- if (update.action === 'already-current') {
616
+ if (update.action === 'rewrote') {
617
+ // With both rewritten, the line above names them; otherwise each says so here.
618
+ const bothRewritten = updates.filter(other => other.action === 'rewrote').length === PROJECT_SKILL_COPY_TARGETS.length;
619
+ if (!bothRewritten || update.backup) {
620
+ printLine(sanitizeSetupHumanInlineText(`Rewrote ${update.path} from the installed CLI skill${describeBackup(update.backup)}`));
621
+ }
622
+ }
623
+ else if (update.action === 'already-current') {
644
624
  printLine(`Current ${update.path}`);
645
625
  }
646
626
  else if (update.action === 'would-update') {
@@ -650,8 +630,10 @@ async function skillsUpdateCommand() {
650
630
  printLine(`Relinked ${update.path}`);
651
631
  }
652
632
  else {
653
- printLine(`Updated ${update.path}`);
633
+ printLine(sanitizeSetupHumanInlineText(`Updated ${update.path}${describeBackup(update.backup)}`));
654
634
  }
635
+ if (update.warning)
636
+ printLine(sanitizeSetupHumanInlineText(`Warning: ${update.warning}`));
655
637
  }
656
638
  }
657
639
  function skillsPathCommand() {
@@ -684,7 +666,7 @@ async function resolveSetupTeam(teamSlugArg, createTeamName, noInput, dryRun, wo
684
666
  throw new Error('--create-team requires a non-empty team name.');
685
667
  return await createSetupTeam(name, request);
686
668
  }
687
- const teams = await fetchTeams(request);
669
+ const teams = await fetchTeams(request, datasetTransferTimeoutMs());
688
670
  const normalizedTeamSlug = teamSlugArg ? normalizeSlugInput(teamSlugArg) : null;
689
671
  if (normalizedTeamSlug) {
690
672
  const team = teams.find(candidate => candidate.slug === normalizedTeamSlug);
@@ -731,7 +713,7 @@ async function resolveSetupProjects(team, projectSlugArg, createProjectName, noI
731
713
  if (createProjectName && !canCreateProject) {
732
714
  throw new Error(`Only team admins can create projects in '${team.slug}'. Choose an existing project or ask a team admin to create one.`);
733
715
  }
734
- const projects = await fetchProjects(team.slug, request);
716
+ const projects = await fetchProjects(team.slug, request, datasetTransferTimeoutMs());
735
717
  if (createProjectName) {
736
718
  if (dryRun) {
737
719
  throw new Error('--create-project cannot be used with --dry-run because setup cannot preview a server-generated project slug.');
@@ -926,7 +908,7 @@ async function setupCommand() {
926
908
  throw new Error(`This directory is already an Orizu workspace for team '${workspaceTeamSlug}'. Run setup in another directory to set up team '${setupTeam.slug}'.`);
927
909
  }
928
910
  if (fix) {
929
- setupProjects = await fetchProjects(setupTeam.slug, setupRequest);
911
+ setupProjects = await fetchProjects(setupTeam.slug, setupRequest, datasetTransferTimeoutMs());
930
912
  if (setupProjects.length === 0)
931
913
  throw new Error(`Team '${setupTeam.slug}' has no projects to restore.`);
932
914
  }
@@ -1052,7 +1034,7 @@ async function setupCommand() {
1052
1034
  if (dryRun) {
1053
1035
  printLine('Dry run: no skill files were changed.');
1054
1036
  }
1055
- const legacy = replaceLegacySkillInstalls({
1037
+ const legacy = await replaceLegacySkillInstalls({
1056
1038
  ...legacyScopeAfterInstall(targets, installOutcomes, dryRun),
1057
1039
  mode: installMode,
1058
1040
  dryRun,
@@ -1070,7 +1052,9 @@ async function setupCommand() {
1070
1052
  printLine(`${label} skipped`);
1071
1053
  }
1072
1054
  else {
1073
- printLine(`${label} installed`);
1055
+ printLine(sanitizeSetupHumanInlineText(`${label} installed${describeBackup(outcome.backup)}`));
1056
+ if (outcome.warning)
1057
+ printLine(sanitizeSetupHumanInlineText(`Warning: ${outcome.warning}`));
1074
1058
  }
1075
1059
  }
1076
1060
  }
@@ -1165,6 +1149,8 @@ async function setupCommand() {
1165
1149
  action: outcome.action,
1166
1150
  mode: outcome.mode,
1167
1151
  error: outcome.error,
1152
+ ...(outcome.backup ? { backup: outcome.backup } : {}),
1153
+ ...(outcome.warning ? { warning: outcome.warning } : {}),
1168
1154
  })),
1169
1155
  skill: {
1170
1156
  root: source.root,
@@ -0,0 +1,23 @@
1
+ export interface ReplaceSkillFolderInput {
2
+ sourceRoot: string;
3
+ targetPath: string;
4
+ mode: 'link' | 'copy';
5
+ /** Where a person's own folder is moved; no agent looks for skills there. */
6
+ backupPath: string;
7
+ /** Writes Orizu's marker into the new copy before it is put in place. */
8
+ writeMarker: (dir: string) => void;
9
+ }
10
+ export interface ReplaceSkillFolderResult {
11
+ /** Where a person's own folder was moved, when there was one. */
12
+ backup: string | null;
13
+ /** Why a person's folder, or part of it, is still beside the skill. */
14
+ warning?: string;
15
+ }
16
+ /** One replacement whose renames go through `move`, so a test can make one fail. */
17
+ export declare function replaceSkillFolderUsing(move: (from: string, to: string) => Promise<void>, input: ReplaceSkillFolderInput): Promise<ReplaceSkillFolderResult>;
18
+ /**
19
+ * Runs one replacement. Ctrl-C or SIGTERM interrupts it; once staging is
20
+ * removed and the old folder is back, the process ends by that same signal.
21
+ */
22
+ export declare function runSkillFolderReplacement(input: ReplaceSkillFolderInput): Promise<ReplaceSkillFolderResult>;
23
+ //# sourceMappingURL=skill-install-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-install-workflow.d.ts","sourceRoot":"","sources":["../src/skill-install-workflow.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC,iEAAiE;IACjE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuJD,oFAAoF;AACpF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAElK;AAaD;;;GAGG;AACH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAgBjH"}
@@ -0,0 +1,184 @@
1
+ // Reads the skill source and the install target it is given.
2
+ // @tia-reads: skills/orizu
3
+ // Replaces one skill folder without ever leaving a half-written one, and never
4
+ // deletes a person's files: they are moved to a backup first. Loaded only
5
+ // through `await import(...)`, so `orizu skills path` never loads Effect.
6
+ import { cp, lstat, mkdir, readdir, readlink, readFile, rename, rm, symlink, unlink, copyFile } from 'node:fs/promises';
7
+ import { basename, dirname, join, resolve } from 'node:path';
8
+ import { Context, Effect, Exit, Option, Schema } from 'effect';
9
+ import { computeSkillContentHash, SKILL_META_FILENAME, shouldCopySkillPath } from './skill-installer.js';
10
+ /** Every rename the replacement makes. Tests swap it to make one rename fail. */
11
+ const SkillFolderRename = Context.Reference('orizu/SkillFolderRename', {
12
+ defaultValue: () => rename,
13
+ });
14
+ const renameEntry = (from, to) => Effect.gen(function* () {
15
+ const move = yield* SkillFolderRename;
16
+ yield* attempt(() => move(from, to));
17
+ });
18
+ const SkillMarker = Schema.Struct({ skillHash: Schema.String });
19
+ const attempt = (run) =>
20
+ // One file step at a time: an interruption lands between steps, never during one.
21
+ Effect.uninterruptible(Effect.tryPromise({ try: run, catch: cause => cause instanceof Error ? cause : new Error(String(cause)) }));
22
+ const exists = (path) => attempt(() => lstat(path).then(() => true, () => false));
23
+ // Orizu's own install is a link, or a copy whose files still match its marker
24
+ // or the skill being installed. Anything else may hold a person's work.
25
+ function classify(targetPath, sourceRoot) {
26
+ return Effect.gen(function* () {
27
+ const stats = yield* attempt(() => lstat(targetPath).then(found => found, () => null));
28
+ if (!stats)
29
+ return 'missing';
30
+ if (stats.isSymbolicLink())
31
+ return 'link';
32
+ if (!stats.isDirectory())
33
+ return 'person';
34
+ const marker = yield* attempt(() => readFile(join(targetPath, SKILL_META_FILENAME), 'utf8').then(text => JSON.parse(text), () => null));
35
+ const installedHash = computeSkillContentHash(targetPath);
36
+ const recorded = Schema.decodeUnknownOption(SkillMarker)(marker);
37
+ if (Option.isSome(recorded) && recorded.value.skillHash === installedHash)
38
+ return 'orizu';
39
+ return installedHash === computeSkillContentHash(sourceRoot) ? 'orizu' : 'person';
40
+ });
41
+ }
42
+ function copyTree(root, from, to) {
43
+ return Effect.gen(function* () {
44
+ yield* attempt(() => mkdir(to, { recursive: true }));
45
+ const entries = yield* attempt(() => readdir(from, { withFileTypes: true }));
46
+ for (const entry of entries) {
47
+ const source = join(from, entry.name);
48
+ const target = join(to, entry.name);
49
+ if (!shouldCopySkillPath(root, source))
50
+ continue;
51
+ if (entry.isDirectory())
52
+ yield* copyTree(root, source, target);
53
+ else if (entry.isFile())
54
+ yield* attempt(() => copyFile(source, target));
55
+ else if (entry.isSymbolicLink())
56
+ yield* attempt(async () => symlink(await readlink(source), target));
57
+ else
58
+ yield* Effect.fail(new Error(`${source} is not a file or folder, so the skill can't be copied.`));
59
+ }
60
+ });
61
+ }
62
+ // Links are removed as links, never by following them.
63
+ const removeEntry = (path) => attempt(async () => {
64
+ const stats = await lstat(path).catch(() => null);
65
+ if (!stats)
66
+ return;
67
+ if (stats.isSymbolicLink())
68
+ await unlink(path);
69
+ else
70
+ await rm(path, { recursive: true, force: true });
71
+ });
72
+ /**
73
+ * Moves a person's folder to the backup. Once the copy is whole, the backup
74
+ * is reported even when the original cannot be removed; if nothing could be
75
+ * moved, the folder stays where it is and the warning says why.
76
+ */
77
+ function moveToBackup(from, to) {
78
+ const stayed = (cause) => ({
79
+ backup: resolve(from),
80
+ warning: `Could not move your earlier folder to ${to}: ${cause.message}. It is still at ${from}, where agents may load it as a second skill; move it somewhere else.`,
81
+ });
82
+ return Effect.gen(function* () {
83
+ yield* attempt(() => mkdir(dirname(to), { recursive: true }));
84
+ // A project on another disk cannot be renamed into the home folder, so it is copied.
85
+ const renamed = yield* renameEntry(from, to).pipe(Effect.as(true), Effect.catch(error => error.code === 'EXDEV' ? Effect.succeed(false) : Effect.fail(error)));
86
+ if (renamed)
87
+ return { backup: resolve(to) };
88
+ // Our own half-made copy is removed; the person's folder is untouched.
89
+ yield* attempt(() => cp(from, to, { recursive: true, verbatimSymlinks: true })).pipe(Effect.tapError(() => removeEntry(to).pipe(Effect.ignore)));
90
+ return yield* attempt(() => rm(from, { recursive: true })).pipe(Effect.as({ backup: resolve(to) }), Effect.catch(cause => Effect.succeed({
91
+ backup: resolve(to),
92
+ warning: `Copied your earlier folder to ${to}, but could not remove it from ${from}: ${cause.message}. Agents may load it there as a second skill; remove it once you have checked the copy.`,
93
+ })));
94
+ }).pipe(Effect.catch(cause => Effect.succeed(stayed(cause))));
95
+ }
96
+ /**
97
+ * Stage the new skill beside the target, move the old folder aside, rename the
98
+ * staged one into place, then drop Orizu's old copy or back up a person's.
99
+ * On a failure or an interruption, staging is removed and the old folder is
100
+ * put back.
101
+ */
102
+ function replaceSkillFolder(input) {
103
+ const { sourceRoot, targetPath, mode } = input;
104
+ const id = `${process.pid}-${Date.now().toString(36)}`;
105
+ const name = basename(targetPath);
106
+ const staging = join(dirname(targetPath), `${name}.orizu-staging-${id}`);
107
+ const aside = join(dirname(targetPath), `${name}.orizu-previous-${id}`);
108
+ // Set when the old folder could not be put back after a failure.
109
+ let strandedAt = null;
110
+ // Only the copy into staging can be interrupted. Once the old folder moves
111
+ // aside, the swap and the clean-up after it always finish, so Ctrl-C can
112
+ // never skip putting the old folder back or moving a person's folder out.
113
+ return Effect.uninterruptibleMask(restore => Effect.gen(function* () {
114
+ const existing = yield* classify(targetPath, sourceRoot);
115
+ yield* attempt(() => mkdir(dirname(targetPath), { recursive: true }));
116
+ const swapped = yield* Effect.scoped(Effect.gen(function* () {
117
+ // Registered before staging starts, so a copy that stops part way is removed too.
118
+ // Gone already once it has been renamed into place.
119
+ yield* Effect.addFinalizer(() => removeEntry(staging).pipe(Effect.ignore));
120
+ yield* restore(mode === 'link'
121
+ ? attempt(() => symlink(sourceRoot, staging, 'dir'))
122
+ : copyTree(sourceRoot, sourceRoot, staging).pipe(Effect.andThen(Effect.try({ try: () => input.writeMarker(staging), catch: cause => cause instanceof Error ? cause : new Error(String(cause)) }))));
123
+ if (existing !== 'missing') {
124
+ yield* Effect.acquireRelease(renameEntry(targetPath, aside), (_, exit) => Exit.isSuccess(exit)
125
+ ? Effect.void
126
+ // Put the old folder back unless something already holds its place.
127
+ : exists(targetPath).pipe(Effect.flatMap(taken => taken ? Effect.fail(new Error(`${targetPath} is taken`)) : renameEntry(aside, targetPath)), Effect.catch(() => Effect.sync(() => { strandedAt = aside; }))));
128
+ }
129
+ yield* renameEntry(staging, targetPath);
130
+ return existing;
131
+ })).pipe(Effect.mapError(error => strandedAt
132
+ ? new Error(`${error.message} Your earlier folder could not be put back at ${targetPath}; it is at ${strandedAt}.`)
133
+ : error));
134
+ if (swapped === 'missing')
135
+ return { backup: null };
136
+ if (swapped !== 'person') {
137
+ yield* removeEntry(aside).pipe(Effect.mapError(cause => new Error(`Installed ${targetPath}, but could not remove the earlier copy at ${aside}: ${cause.message}`)));
138
+ return { backup: null };
139
+ }
140
+ // The person's folder is safe beside the target; move it out of agents' sight.
141
+ return yield* moveToBackup(aside, input.backupPath);
142
+ }));
143
+ }
144
+ /** One replacement whose renames go through `move`, so a test can make one fail. */
145
+ export async function replaceSkillFolderUsing(move, input) {
146
+ return await Effect.runPromise(replaceSkillFolder(input).pipe(Effect.provideService(SkillFolderRename, move)));
147
+ }
148
+ const STOP_SIGNALS = ['SIGINT', 'SIGTERM'];
149
+ /** Ends the process by the signal that stopped it, once its handlers are gone. */
150
+ async function endBy(signal, handlers) {
151
+ for (const [name, handler] of handlers)
152
+ process.removeListener(name, handler);
153
+ process.kill(process.pid, signal);
154
+ // The signal ends the process; if another listener holds it, exit as a shell would.
155
+ setTimeout(() => process.exit(signal === 'SIGINT' ? 130 : 143), 1_000);
156
+ return await new Promise(() => { });
157
+ }
158
+ /**
159
+ * Runs one replacement. Ctrl-C or SIGTERM interrupts it; once staging is
160
+ * removed and the old folder is back, the process ends by that same signal.
161
+ */
162
+ export async function runSkillFolderReplacement(input) {
163
+ const controller = new AbortController();
164
+ const stop = { by: null };
165
+ const handlers = STOP_SIGNALS.map(signal => [signal, () => { stop.by = signal; controller.abort(); }]);
166
+ for (const [signal, handler] of handlers)
167
+ process.once(signal, handler);
168
+ try {
169
+ const result = await Effect.runPromise(replaceSkillFolder(input), { signal: controller.signal });
170
+ // A stop that arrived as the replacement finished still stops the command.
171
+ if (stop.by)
172
+ return await endBy(stop.by, handlers);
173
+ return result;
174
+ }
175
+ catch (error) {
176
+ if (!stop.by)
177
+ throw error;
178
+ return await endBy(stop.by, handlers);
179
+ }
180
+ finally {
181
+ for (const [signal, handler] of handlers)
182
+ process.removeListener(signal, handler);
183
+ }
184
+ }
@@ -21,6 +21,10 @@ export interface SkillInstallResult {
21
21
  path: string;
22
22
  action: 'created' | 'updated' | 'would-create' | 'would-update';
23
23
  mode: 'link' | 'copy' | 'section';
24
+ /** Where a person's own folder at the path was moved before replacing it. */
25
+ backup?: string;
26
+ /** Why a person's folder, or part of it, is still beside the skill. */
27
+ warning?: string;
24
28
  }
25
29
  export interface SkillInstallMeta {
26
30
  name: string;
@@ -65,9 +69,10 @@ export declare function compareProjectSkillCopies(options?: SkillInstallOptions)
65
69
  export declare function isProjectLevelTarget(target: SkillInstallTarget): boolean;
66
70
  export declare function isStableSkillSourcePath(root: string): boolean;
67
71
  export declare function resolveEffectiveInstallMode(target: SkillInstallTarget, sourceRoot: string, mode?: SkillInstallMode): 'link' | 'copy' | 'section';
72
+ export declare function shouldCopySkillPath(sourceRoot: string, sourcePath: string): boolean;
68
73
  export declare function targetNeedsOverwrite(target: SkillInstallTarget, options?: SkillInstallOptions): boolean;
69
74
  export declare function readSkillMeta(targetDir: string): SkillInstallMeta | null;
70
- export declare function installSkillTarget(target: SkillInstallTarget, options?: SkillInstallOptions): SkillInstallResult;
75
+ export declare function installSkillTarget(target: SkillInstallTarget, options?: SkillInstallOptions): Promise<SkillInstallResult>;
71
76
  export type SkillTargetState = 'current' | 'stale' | 'missing' | 'broken-link' | 'unmanaged';
72
77
  export interface SkillTargetStatus {
73
78
  target: SkillInstallTarget;
@@ -109,8 +114,8 @@ export interface LegacySkillReplaceOptions extends SkillInstallOptions {
109
114
  heldBack?: Partial<Record<SkillInstallTarget, string>>;
110
115
  }
111
116
  export declare function findLegacySkillInstalls(options?: SkillInstallOptions): LegacySkillScan;
112
- export declare function replaceLegacySkillInstalls(options?: LegacySkillReplaceOptions): {
117
+ export declare function replaceLegacySkillInstalls(options?: LegacySkillReplaceOptions): Promise<{
113
118
  replacements: LegacySkillReplacement[];
114
119
  problems: LegacySkillProblem[];
115
- };
120
+ }>;
116
121
  //# sourceMappingURL=skill-installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill-installer.d.ts","sourceRoot":"","sources":["../src/skill-installer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skill-installer.d.ts","sourceRoot":"","sources":["../src/skill-installer.ts"],"names":[],"mappings":"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"}