@hasna/instructions 0.7.0 → 0.7.2
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/README.md +52 -11
- package/dist/chunks/{apply-7svah2qm.js → apply-ps6jjt6k.js} +3 -3
- package/dist/chunks/{apply-jp13sa1e.js → apply-wj7wh5km.js} +3 -3
- package/dist/chunks/{index-1cnyjnd7.js → index-18j7x3xw.js} +2 -2
- package/dist/chunks/{index-3djthcm6.js → index-27az1qb1.js} +28 -1
- package/dist/chunks/{index-fwbs6tma.js → index-ape24zc0.js} +2 -2
- package/dist/chunks/{index-bzyxvwcd.js → index-ek15201v.js} +2 -2
- package/dist/chunks/{index-arrrx0ed.js → index-mvckkf15.js} +39 -20
- package/dist/chunks/{index-7m4j8a33.js → index-n6nh6f5r.js} +235 -20
- package/dist/chunks/{index-4pfq42t5.js → index-pq18fdbj.js} +146 -32
- package/dist/chunks/{index-n3fjspr0.js → index-preyvj8n.js} +28 -1
- package/dist/chunks/{index-z3ma4aqk.js → index-y8410ve0.js} +3 -3
- package/dist/chunks/{local-5wvv5xv3.js → local-1c1qxzvx.js} +155 -4
- package/dist/chunks/{local-zxa5dpf4.js → local-hc67bynj.js} +155 -4
- package/dist/chunks/{s3-backup-n2z4fwyh.js → s3-backup-hxe3zpfz.js} +1 -1
- package/dist/chunks/{s3-object-store-90tgbh6k.js → s3-object-store-n0s82swt.js} +1 -1
- package/dist/chunks/{sync-r8t7zsp3.js → sync-12m3vs6g.js} +4 -4
- package/dist/chunks/{sync-2075deve.js → sync-azd9t4p8.js} +4 -4
- package/dist/chunks/{template-wgnzdn7h.js → template-e4qrxs8p.js} +1 -1
- package/dist/chunks/{template-fsfxpe4p.js → template-nhn124zd.js} +1 -1
- package/dist/cli/index.js +860 -181
- package/dist/data/config-store.d.ts +22 -6
- package/dist/data/config-store.d.ts.map +1 -1
- package/dist/db/configs.d.ts +4 -1
- package/dist/db/configs.d.ts.map +1 -1
- package/dist/db/local.d.ts +1 -1
- package/dist/db/local.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +940 -134
- package/dist/lib/asset-plan.d.ts.map +1 -1
- package/dist/lib/codewith-shared-todos-storage-standard.d.ts.map +1 -1
- package/dist/lib/compact-output.d.ts +2 -18
- package/dist/lib/compact-output.d.ts.map +1 -1
- package/dist/lib/dangerous-operation-guard-standard.d.ts.map +1 -1
- package/dist/lib/export.d.ts.map +1 -1
- package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
- package/dist/lib/global-source-coverage.d.ts +12 -7
- package/dist/lib/global-source-coverage.d.ts.map +1 -1
- package/dist/lib/import.d.ts.map +1 -1
- package/dist/lib/instruction-graph.d.ts +1 -1
- package/dist/lib/instruction-graph.d.ts.map +1 -1
- package/dist/lib/instruction-source-policy.d.ts +6 -0
- package/dist/lib/instruction-source-policy.d.ts.map +1 -0
- package/dist/lib/platform-profiles.d.ts.map +1 -1
- package/dist/lib/project-dashboard-standard.d.ts.map +1 -1
- package/dist/lib/session-apply.d.ts +24 -0
- package/dist/lib/session-apply.d.ts.map +1 -1
- package/dist/lib/session-refresh.d.ts +30 -0
- package/dist/lib/session-refresh.d.ts.map +1 -0
- package/dist/lib/session-render.d.ts +34 -1
- package/dist/lib/session-render.d.ts.map +1 -1
- package/dist/lib/transforms.d.ts +1 -0
- package/dist/lib/transforms.d.ts.map +1 -1
- package/dist/mcp/index.js +23 -22
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/sdk/index.js +46 -3
- package/dist/sdk/v1-client.d.ts +10 -3
- package/dist/sdk/v1-client.d.ts.map +1 -1
- package/dist/sdk/v1.generated.d.ts +52 -2
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +215 -15
- package/dist/server/openapi.d.ts +240 -3
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/cloud-store.d.ts +2 -15
- package/dist/storage/cloud-store.d.ts.map +1 -1
- package/dist/storage/index.js +39 -20
- package/dist/storage/s3-object-store.d.ts.map +1 -1
- package/dist/types/index.d.ts +49 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -52,9 +52,13 @@ existing row.
|
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
instructions --help
|
|
55
|
-
instructions list
|
|
56
|
-
instructions list --verbose
|
|
57
|
-
instructions list --json
|
|
55
|
+
instructions list # compact, paged summary
|
|
56
|
+
instructions list --verbose # expanded metadata, still paged
|
|
57
|
+
instructions list --json # legacy complete full-record array
|
|
58
|
+
instructions list --json --detail compact --limit 20 # bounded identity envelope
|
|
59
|
+
instructions list --json --detail compact --fields id,slug,name,version
|
|
60
|
+
instructions list --json --detail full --limit 20 # explicit bounded content read
|
|
61
|
+
instructions list --json --detail compact --all # explicit complete metadata read
|
|
58
62
|
instructions show <slug> # full metadata + content
|
|
59
63
|
instructions inspect <slug> # alias for show
|
|
60
64
|
instructions profile resolve
|
|
@@ -68,7 +72,9 @@ small. Human output is capped at 20 rows unless you pass `--limit`; use
|
|
|
68
72
|
`--cursor` to continue from the next page. Detail is explicit:
|
|
69
73
|
|
|
70
74
|
- `--verbose` expands list rows with descriptions, tags, and paths.
|
|
71
|
-
- `--json` preserves full
|
|
75
|
+
- Legacy `--json` with no modern detail flags preserves the complete full-record array for automation. Explicit `--limit`/`--cursor` now bound that legacy array.
|
|
76
|
+
- `--detail compact` returns a content-free `{ configs, _meta }` identity envelope through the already-deployed `/v1/configs?view=identity` projection. Add `--fields` (including immutable `id`), `--all`, or `--pretty` as needed.
|
|
77
|
+
- `--detail full` is the explicit content-bearing envelope; it is bounded unless `--all` is supplied.
|
|
72
78
|
- `show`/`inspect` and `snapshot show` print full config or snapshot content.
|
|
73
79
|
|
|
74
80
|
`instructions report --json` emits the stable `schema_version: 1` report
|
|
@@ -77,6 +83,24 @@ envelope. Its top-level fields are `configs`, `profiles`, `drift`, `secrets`,
|
|
|
77
83
|
`secrets.policy` is `redacted_on_ingest`. Run `instructions report` without
|
|
78
84
|
`--json` for the human-readable report.
|
|
79
85
|
|
|
86
|
+
### Conditional config updates
|
|
87
|
+
|
|
88
|
+
Read the current config version before preparing an edit, then pass that
|
|
89
|
+
`expected_version` to `updateConfig` (store/public SDK), `update_config` (MCP), or:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
instructions add ./rule.md --kind reference --name example-rule --update --expected-version 7
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Guarded clients use `POST /v1/configs/:id/conditional-update`, which requires
|
|
96
|
+
`expected_version`. The generated SDK also exposes `conditionalUpdateConfig` for
|
|
97
|
+
this operation. An older server returns 404 without mutation; clients never fall
|
|
98
|
+
back to an unconditional update. A version mismatch returns HTTP 409 with code
|
|
99
|
+
`CONFIG_VERSION_CONFLICT`, changing neither the config nor its snapshots. Refresh
|
|
100
|
+
and review the newer version before trying again. Calls without a precondition
|
|
101
|
+
retain their existing behavior. New servers also accept `expected_version` on
|
|
102
|
+
PATCH/PUT; use the dedicated conditional route when server capabilities are unknown.
|
|
103
|
+
|
|
80
104
|
## Package-Manager Secret Guard
|
|
81
105
|
|
|
82
106
|
`instructions package-manager-scan` blocks package-manager credential ingress without
|
|
@@ -330,6 +354,14 @@ credentials may come from Bun's standard AWS chain (including an ECS task role);
|
|
|
330
354
|
explicit static credentials are optional and must be a complete pair. Status
|
|
331
355
|
never prints credential values or the bucket name.
|
|
332
356
|
|
|
357
|
+
The production deployment runs the immutable predeploy backup push and verify
|
|
358
|
+
as direct Bun commands in the exact candidate image. The GitHub runner alone
|
|
359
|
+
parses the push receipt, then supplies the recorded payload and manifest version
|
|
360
|
+
IDs to verification; it also checks the receipt digest and size, COMPLIANCE
|
|
361
|
+
Object Lock retention for both versions, and an exact versioned-payload
|
|
362
|
+
readback. Every backup gate completes before any ECR push, database migration,
|
|
363
|
+
or ECS update.
|
|
364
|
+
|
|
333
365
|
The public importable surface is available at `@hasna/instructions/storage`.
|
|
334
366
|
|
|
335
367
|
## Data Directory
|
|
@@ -347,13 +379,20 @@ upgrade.
|
|
|
347
379
|
|
|
348
380
|
`instructions session plan` and `instructions session apply` render
|
|
349
381
|
OpenIdentities and instruction sources into provider-native files for Claude,
|
|
350
|
-
Codex, Cursor, OpenCode, Codewith, Qwen, aicopilot, and Google Antigravity.
|
|
382
|
+
Codex, Cursor, OpenCode, Sumi, Codewith, Qwen, aicopilot, and Google Antigravity.
|
|
351
383
|
The old Google agent target is removed; Antigravity is the only Google coding
|
|
352
384
|
agent render target. Antigravity workspace rules are rendered to
|
|
353
385
|
`.agents/rules/*.md`; its current global rules and MCP files use Google's
|
|
354
386
|
legacy-named `~/.gemini/GEMINI.md` and `~/.gemini/config/mcp_config.json`
|
|
355
387
|
paths but remain owned by the `antigravity` target.
|
|
356
388
|
|
|
389
|
+
Sumi renders one flattened `AGENTS.md`. Pass its actual config directory from
|
|
390
|
+
`sumi debug paths config` as `--target-home`, or use `--project-root` for
|
|
391
|
+
repository instructions. Global plans return `SUMI_CONFIG_DIR`; project plans
|
|
392
|
+
do not change the global config environment. No OpenCode config or fragment
|
|
393
|
+
imports are written. See [session rendering](docs/session-rendering.md) for
|
|
394
|
+
capability limits and examples.
|
|
395
|
+
|
|
357
396
|
Qwen Code session rendering writes `QWEN.md` instructional context with
|
|
358
397
|
`QWEN_HOME` pointing at the rendered profile home. Known config sync also
|
|
359
398
|
tracks Qwen Code `QWEN.md` and `settings.json` files at `~/.qwen/...` and
|
|
@@ -522,14 +561,16 @@ and prior on-box-only evidence must be explicitly reconciled before use.
|
|
|
522
561
|
|
|
523
562
|
## Machine-aware Profiles
|
|
524
563
|
|
|
525
|
-
`instructions init` seeds two platform profiles:
|
|
564
|
+
`instructions init` seeds two empty platform profiles:
|
|
526
565
|
|
|
527
|
-
- `linux-arm64` for
|
|
528
|
-
- `macos-arm64` for
|
|
566
|
+
- `linux-arm64` for Linux arm64
|
|
567
|
+
- `macos-arm64` for macOS arm64
|
|
529
568
|
|
|
530
|
-
|
|
531
|
-
`{{BUN_BIN_DIR}}`, `{{BUN_PATH}}`, and
|
|
532
|
-
|
|
569
|
+
The public presets contain no fleet hostnames or workspace paths. They resolve
|
|
570
|
+
portable tool variables such as `{{BUN_BIN_DIR}}`, `{{BUN_PATH}}`, and
|
|
571
|
+
`{{PATH_PREFIX}}`. Instruction membership is never bulk-copied from the config
|
|
572
|
+
registry; bind reviewed provider/project/role sources explicitly before compiling
|
|
573
|
+
a prompt profile.
|
|
533
574
|
|
|
534
575
|
They also include the project channel variable used by agent-managed project
|
|
535
576
|
workflows:
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
getConfigHome,
|
|
9
9
|
normalizeTargetPath,
|
|
10
10
|
previewConfigs
|
|
11
|
-
} from "./index-
|
|
12
|
-
import"./index-
|
|
11
|
+
} from "./index-ape24zc0.js";
|
|
12
|
+
import"./index-pq18fdbj.js";
|
|
13
13
|
import"./index-35z65jsa.js";
|
|
14
|
-
import"./index-
|
|
14
|
+
import"./index-27az1qb1.js";
|
|
15
15
|
import"./index-nnwp92tk.js";
|
|
16
16
|
export {
|
|
17
17
|
previewConfigs,
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
getConfigHome,
|
|
9
9
|
normalizeTargetPath,
|
|
10
10
|
previewConfigs
|
|
11
|
-
} from "./index-
|
|
12
|
-
import"./index-
|
|
13
|
-
import"./index-
|
|
11
|
+
} from "./index-y8410ve0.js";
|
|
12
|
+
import"./index-n6nh6f5r.js";
|
|
13
|
+
import"./index-preyvj8n.js";
|
|
14
14
|
import"./index-fpeyzn3n.js";
|
|
15
15
|
import"./index-bsan4c16.js";
|
|
16
16
|
export {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
expandPath,
|
|
6
6
|
getConfigHome,
|
|
7
7
|
normalizeTargetPath
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-y8410ve0.js";
|
|
9
9
|
import {
|
|
10
10
|
applyTransform,
|
|
11
11
|
detectMachineContext,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolveConfigStore,
|
|
17
17
|
retiredOrUnsupportedAgentReason,
|
|
18
18
|
templateizeMachineContent
|
|
19
|
-
} from "./index-
|
|
19
|
+
} from "./index-n6nh6f5r.js";
|
|
20
20
|
|
|
21
21
|
// src/lib/sync.ts
|
|
22
22
|
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
|
|
@@ -4,6 +4,7 @@ var CONFIG_AGENTS = [
|
|
|
4
4
|
"claude",
|
|
5
5
|
"codex",
|
|
6
6
|
"opencode",
|
|
7
|
+
"sumi",
|
|
7
8
|
"cursor",
|
|
8
9
|
"codewith",
|
|
9
10
|
"aicopilot",
|
|
@@ -28,6 +29,32 @@ var ASSET_DESTINATION_STRATEGIES = ["emit-file", "install-local", "install-marke
|
|
|
28
29
|
var ASSET_SCOPES = ["global", "project", "session"];
|
|
29
30
|
var ASSET_UNINSTALL_POLICIES = ["remove-managed", "retain"];
|
|
30
31
|
var ASSET_ROLLBACK_POLICIES = ["snapshot", "installer-receipt", "none"];
|
|
32
|
+
class InvalidExpectedVersionError extends Error {
|
|
33
|
+
code = "INVALID_EXPECTED_VERSION";
|
|
34
|
+
constructor() {
|
|
35
|
+
super("expected_version must be a positive safe integer");
|
|
36
|
+
this.name = "InvalidExpectedVersionError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validateExpectedConfigVersion(value) {
|
|
40
|
+
if (value !== undefined && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)) {
|
|
41
|
+
throw new InvalidExpectedVersionError;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class ConfigVersionConflictError extends Error {
|
|
46
|
+
config_id;
|
|
47
|
+
expected_version;
|
|
48
|
+
code = "CONFIG_VERSION_CONFLICT";
|
|
49
|
+
status = 409;
|
|
50
|
+
constructor(config_id, expected_version) {
|
|
51
|
+
super(`Config version conflict: ${config_id} no longer has expected version ${expected_version}`);
|
|
52
|
+
this.config_id = config_id;
|
|
53
|
+
this.expected_version = expected_version;
|
|
54
|
+
this.name = "ConfigVersionConflictError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
class ConfigNotFoundError extends Error {
|
|
32
59
|
constructor(id) {
|
|
33
60
|
super(`Config not found: ${id}`);
|
|
@@ -114,4 +141,4 @@ function isTemplate(content) {
|
|
|
114
141
|
return VAR_PATTERN.test(content);
|
|
115
142
|
}
|
|
116
143
|
|
|
117
|
-
export { CONFIG_AGENTS, PROFILE_CONFIG_BINDING_SCHEMA, INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA, ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, parseTemplateVars, extractTemplateVars, renderTemplate, renderTemplatePreview, isTemplate };
|
|
144
|
+
export { CONFIG_AGENTS, PROFILE_CONFIG_BINDING_SCHEMA, INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA, ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, validateExpectedConfigVersion, ConfigVersionConflictError, ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, parseTemplateVars, extractTemplateVars, renderTemplate, renderTemplatePreview, isTemplate };
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
resolveConfigStore,
|
|
16
16
|
resolveProfileVariables,
|
|
17
17
|
stampCursorGlobalAuthorityMarker
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-pq18fdbj.js";
|
|
19
19
|
import {
|
|
20
20
|
isSecretVarName,
|
|
21
21
|
redactFormatForTarget,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
ConfigApplyError,
|
|
26
26
|
parseTemplateVars
|
|
27
|
-
} from "./index-
|
|
27
|
+
} from "./index-27az1qb1.js";
|
|
28
28
|
|
|
29
29
|
// src/lib/apply.ts
|
|
30
30
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "fs";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
expandPath,
|
|
6
6
|
getConfigHome,
|
|
7
7
|
normalizeTargetPath
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-ape24zc0.js";
|
|
9
9
|
import {
|
|
10
10
|
applyTransform,
|
|
11
11
|
detectMachineContext,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveConfigStore,
|
|
14
14
|
retiredOrUnsupportedAgentReason,
|
|
15
15
|
templateizeMachineContent
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-pq18fdbj.js";
|
|
17
17
|
import {
|
|
18
18
|
isSecretVarName,
|
|
19
19
|
redactContent,
|
|
@@ -73,18 +73,20 @@ function createInstructionsS3ObjectStore(config, createClient = (options) => new
|
|
|
73
73
|
return {
|
|
74
74
|
async putIfAbsent(key, bytes, options) {
|
|
75
75
|
assertSafeInstructionsObjectKey(key);
|
|
76
|
+
let credentials;
|
|
77
|
+
try {
|
|
78
|
+
credentials = resolveRequestCredentials(config);
|
|
79
|
+
} catch {
|
|
80
|
+
throw new Error("Instructions S3 conditional object creation failed");
|
|
81
|
+
}
|
|
76
82
|
for (let attempt = 1;attempt <= CONDITIONAL_CREATE_ATTEMPTS; attempt += 1) {
|
|
77
83
|
let response;
|
|
78
84
|
const controller = new AbortController;
|
|
79
85
|
try {
|
|
80
|
-
const
|
|
81
|
-
response = await runWithDeadline(() => requestFetch(url, {
|
|
86
|
+
const signed = signConditionalCreateRequest(config, key, bytes, options.contentType, credentials, new Date);
|
|
87
|
+
response = await runWithDeadline(() => requestFetch(signed.url, {
|
|
82
88
|
method: "PUT",
|
|
83
|
-
headers:
|
|
84
|
-
"content-type": options.contentType,
|
|
85
|
-
"content-md5": createHash("md5").update(bytes).digest("base64"),
|
|
86
|
-
"if-none-match": "*"
|
|
87
|
-
},
|
|
89
|
+
headers: signed.headers,
|
|
88
90
|
body: Uint8Array.from(bytes),
|
|
89
91
|
redirect: "error",
|
|
90
92
|
signal: controller.signal
|
|
@@ -220,7 +222,7 @@ function resolveRequestCredentials(config) {
|
|
|
220
222
|
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY?.trim();
|
|
221
223
|
const sessionToken = process.env.AWS_SESSION_TOKEN?.trim();
|
|
222
224
|
if (!accessKeyId || !secretAccessKey) {
|
|
223
|
-
throw new Error("Instructions S3
|
|
225
|
+
throw new Error("Instructions S3 signed requests require explicit or AWS environment credentials");
|
|
224
226
|
}
|
|
225
227
|
return { accessKeyId, secretAccessKey, ...sessionToken ? { sessionToken } : {} };
|
|
226
228
|
}
|
|
@@ -235,25 +237,43 @@ function signVersionedRequest(config, key, versionId, method, credentials, now)
|
|
|
235
237
|
"x-amz-date": amzDate,
|
|
236
238
|
...credentials.sessionToken ? { "x-amz-security-token": credentials.sessionToken } : {}
|
|
237
239
|
});
|
|
238
|
-
const signedHeaderNames = [
|
|
239
|
-
"host",
|
|
240
|
-
"x-amz-content-sha256",
|
|
241
|
-
"x-amz-date",
|
|
242
|
-
...credentials.sessionToken ? ["x-amz-security-token"] : []
|
|
243
|
-
].sort();
|
|
244
|
-
const canonicalHeaders = signedHeaderNames.map((name) => `${name}:${headers.get(name).trim()}
|
|
245
|
-
`).join("");
|
|
246
240
|
const canonicalQuery = `versionId=${awsEncode(versionId)}`;
|
|
241
|
+
authorizeRequest(method, url, canonicalQuery, headers, EMPTY_SHA256, credentials, config.region, shortDate, amzDate);
|
|
242
|
+
return { url: url.toString(), headers };
|
|
243
|
+
}
|
|
244
|
+
function signConditionalCreateRequest(config, key, bytes, contentType, credentials, now) {
|
|
245
|
+
const url = buildObjectUrl(config, key);
|
|
246
|
+
const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
247
|
+
const shortDate = amzDate.slice(0, 8);
|
|
248
|
+
const payloadHash = createHash("sha256").update(bytes).digest("hex");
|
|
249
|
+
const headers = new Headers({
|
|
250
|
+
"content-md5": createHash("md5").update(bytes).digest("base64"),
|
|
251
|
+
"content-type": contentType,
|
|
252
|
+
host: url.host,
|
|
253
|
+
"if-none-match": "*",
|
|
254
|
+
"x-amz-content-sha256": payloadHash,
|
|
255
|
+
"x-amz-date": amzDate,
|
|
256
|
+
...credentials.sessionToken ? { "x-amz-security-token": credentials.sessionToken } : {}
|
|
257
|
+
});
|
|
258
|
+
authorizeRequest("PUT", url, "", headers, payloadHash, credentials, config.region, shortDate, amzDate);
|
|
259
|
+
return { url: url.toString(), headers };
|
|
260
|
+
}
|
|
261
|
+
function authorizeRequest(method, url, canonicalQuery, headers, payloadHash, credentials, region, shortDate, amzDate) {
|
|
262
|
+
const signedHeaderNames = [];
|
|
263
|
+
headers.forEach((_value, name) => signedHeaderNames.push(name));
|
|
264
|
+
signedHeaderNames.sort();
|
|
265
|
+
const canonicalHeaders = signedHeaderNames.map((name) => `${name}:${headers.get(name).trim().replace(/\s+/g, " ")}
|
|
266
|
+
`).join("");
|
|
247
267
|
const canonicalRequest = [
|
|
248
268
|
method,
|
|
249
269
|
url.pathname,
|
|
250
270
|
canonicalQuery,
|
|
251
271
|
canonicalHeaders,
|
|
252
272
|
signedHeaderNames.join(";"),
|
|
253
|
-
|
|
273
|
+
payloadHash
|
|
254
274
|
].join(`
|
|
255
275
|
`);
|
|
256
|
-
const scope = `${shortDate}/${
|
|
276
|
+
const scope = `${shortDate}/${region}/s3/aws4_request`;
|
|
257
277
|
const stringToSign = [
|
|
258
278
|
"AWS4-HMAC-SHA256",
|
|
259
279
|
amzDate,
|
|
@@ -262,12 +282,11 @@ function signVersionedRequest(config, key, versionId, method, credentials, now)
|
|
|
262
282
|
].join(`
|
|
263
283
|
`);
|
|
264
284
|
const dateKey = hmac(`AWS4${credentials.secretAccessKey}`, shortDate);
|
|
265
|
-
const regionKey = hmac(dateKey,
|
|
285
|
+
const regionKey = hmac(dateKey, region);
|
|
266
286
|
const serviceKey = hmac(regionKey, "s3");
|
|
267
287
|
const signingKey = hmac(serviceKey, "aws4_request");
|
|
268
288
|
const signature = createHmac("sha256", signingKey).update(stringToSign).digest("hex");
|
|
269
289
|
headers.set("authorization", `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaderNames.join(";")}, Signature=${signature}`);
|
|
270
|
-
return { url: url.toString(), headers };
|
|
271
290
|
}
|
|
272
291
|
function buildObjectUrl(config, key) {
|
|
273
292
|
const encodedKey = key.split("/").map(awsEncode).join("/");
|